﻿#ifndef FMPEGPLAYER_H
#define FMPEGPLAYER_H

#include "../../Common/Config.h"
#include "../../Common/PacketQueue.h"
#include "../../Common/FrameQueue.h"
#include "../Renderers/RenderManager.h"

#include<QWidget>
#include<QVBoxLayout>

class Decoder
{
public:
    Decoder();
    ~Decoder();
    void DecoderInit(AVCodecContext *avctx, PacketQueue *queue);            //初始化解码类，传递解码器上下文和队列
    int DecoderStart(enum AVMediaType codecType,void* arg);              //创建和使用解码器线程
    int AudioThread(void* arg);                     //音频解码线程
    int VideoThread(void* arg);                     //视频解码线程
    int GetVideoFrame(AVFrame *frame);              //获取视频帧
    int DecoderDecodeFrame(AVFrame *frame);         //解码packet队列并返回帧
    int QueuePicture(FrameQueue *fq, AVFrame *src_frame, double pts,
                      double duration, int64_t pos, int serial);                //把解码后的Frame放入Frame队列
private:
    AVPacket m_Pkt;                         // 用来保存avcodec_send_packet发送失败的packet
    int m_PacketPending;                    // 是否保存了上一个packet，在avcodec_send_packet发送packet到解码器解码失败的时候使用
    PacketQueue	*m_Queue;                   // 数据包队列
    AVCodecContext	*m_Avctx;               // 解码器上下文
    std::thread *m_DecoderThread;           // 解码线程
    int m_PktSerial;                        // 包序列
    int	m_Finished;                         // 为0，表示解码器处于工作状态。为非0，表示解码器处于空闲状态
    int m_DecoderReorderPts;
    int64_t m_NextPts;                      //预计的下一帧的Pts
    AVRational m_NextPtsTb;                 //下一帧的时间基
    int64_t m_StartPts;
    AVRational m_StartPtsTb;
};

class FmpegPlayer;
//将音频Frame数据保存到缓存区，供SdlAudioCallback调用，对音频进行重采样也是在此
static int AudioDecodeFrame(FmpegPlayer *is);
//音频输出回调函数，如果缓存区没有数据了则重新调用AudioDecodeFrame读取数据
static void SdlAudioCallback(void *opaque, Uint8 *stream, int len);

// 时钟相关
//获取时钟
double GetClock(Clock *c);
//设置时钟
void SetClockAt(Clock *c, double pts, int serial, double time);
void SetClock(Clock *c, double pts, int serial);
//初始化时钟
void InitClock(Clock *c, int *queue_serial);

class FmpegPlayer:public QWidget
{
    Q_OBJECT
    // 友元函数的声明
    friend int AudioDecodeFrame(FmpegPlayer *is);
    friend void SdlAudioCallback(void *opaque, Uint8 *stream, int len);
    //声明Decoder类为其友元类，Decoder可访问其private成员
    friend class Decoder;
public:
    explicit FmpegPlayer(QWidget *parent = nullptr):QWidget(parent){}
    FmpegPlayer(int width,int height);
    ~FmpegPlayer();
    int Play();
    void SetUrl(QString url);                       //设置Url
    int StreamOpen();                               //打开流
    void StreamClose();                             //关闭流
    int ReadThread();                               //读取函数
    int VideoRefreshThread();                       //视频刷新函数
    void VideoRefresh(double *remaining_time);      //视频刷新
    int IsRealtime(AVFormatContext * s);            //判断是否是时实流
    int StreamComponentOpen(int streamIndex);       //打开指定stream对应解码器、创建解码线程、以及初始化对应的输出
    //关于判断包队列是否已满，条件有如果留索引不存在默认包队列满，流是一个专辑图片默认队列满，队列包内容播放时间大于1s
    int StreamHasEnoughPackets(AVStream *st, int streamId, PacketQueue *queue);    //判断packet队列是否已满
    void CheckPlayFinish();                         //检查播放是否结束
    //打开音频，配置SDL音频参数
    int AudioOpen(int64_t wantedChannelLayout,
                   int wantedNbChannels, int wantedSampleRate,
                   struct AudioParams *audioHwParams);
    double VpDuration(Frame *vp, Frame *nextvp);
    double ComputeTargetDelay(double delay);
    int GetMasterSyncType();
    double GetMasterClock();
    void  UpdateVideoPts(double pts, int64_t pos, int serial);

private:
    Decoder m_Viddec;                    //视频解码器
    Decoder m_Auddec;                    //音频解码器
    Render *m_Render;                    //当前使用的渲染器
    int m_EOF;                           //读取结束标志
    int m_Width;
    int m_Height;
    QString m_Url;                       //播放文件地址
    int m_SeekByBytes;                   //通过字节进行seek
    int m_VideoStream;                   //视屏流索引
    int m_AudioStream;                   //音频流索引
    AVStream *m_AudioSt = NULL;          //音频流
    AVStream *m_VideoSt = NULL;          //视频流
    int m_Realtime;                      //是否是实时流
    int m_AbortRequest;                  //是否请求退出
    PacketQueue	m_Audioq;                //音频packet队列
    PacketQueue m_Videoq;                //视频packet队列
    FrameQueue	m_Pictq;                 //视频Frame队列
    FrameQueue	m_Sampq;                 //采样Frame队列
    AVFormatContext *m_Ic = NULL;        //格式上下文
    std::thread *m_ReadThread;           //读取线程
    std::thread *m_VideoRefreshThread;   //视频刷新线程
    QVBoxLayout *m_MainLayout;
    int m_VideoNoData;                   //视频队列是否有数据
    int m_AudioNoData;                   //音频队列是否有数据
    int m_ForceRefresh;                  //是否强制视频刷新
    struct AudioParams m_AudioSrc;       //保存最新解码的音频参数
    struct AudioParams m_AudioTgt;       //保存SDL音频输出需要的参数
    struct SwrContext *m_SwrCtx;         //音频重采样context
    int	m_AudioHwBufSize;                //SDL音频缓冲区的大小(字节为单位)
    // 指向待播放的一帧音频数据，指向的数据区将被拷入SDL音频缓冲区。若经过重采样则指向audio_buf1，
    // 否则指向frame中的音频
    uint8_t	*m_AudioBuf;                 // 指向需要重采样的数据
    uint8_t	*m_AudioBuf1;                // 指向重采样后的数据
    unsigned int m_AudioBufSize;         // 待播放的一帧音频数据(audio_buf指向)的大小
    unsigned int m_AudioBuf1Size;        // 申请到的音频缓冲区audio_buf1的实际尺寸
    int	m_AudioBufIndex;                 // 更新拷贝位置 当前音频帧中已拷入SDL音频缓冲区
    int m_AudioWriteBufSize;
    int m_AudioVolum;                          // 音量相关
    int m_StartupVolume;                       // 起始音量
    int64_t m_AudioCallbackTime;               // SDL调用回调的时间
    Clock	m_Audclk;                   // 音频时钟
    Clock	m_Vidclk;                   // 视频时钟
    double  m_AudioClock = 0;            // 当前音频帧的PTS+当前帧Duration
    int     m_AudioClockSerial;      // 播放序列，seek可改变此值, 解码后保存
    double  m_FrameTimer = 0;
    int     m_AvSyncType = AV_SYNC_AUDIO_MASTER;           // 音视频同步类型, 默认audio master
    //单步运行
    int m_Step = 0;
    int m_Framedrop = 1;
    int m_FrameDropsLate = 0;
};

#endif // FMPEGPLAYER_H
